﻿using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
using CTTool.Web.Models;

namespace CTTool.Web.Controllers
{
    public class ConvertItem
    {
        public ConvertItem()
        {
            this.modelDic = new Dictionary<string, StringBuilder>();
        }
        public string MD5 { get; set; }
        public JObject JsonObject { get; set; }
        public string DownloadPath { get; set; }
        public Dictionary<string, StringBuilder> modelDic { get; set; }
    }

    public static class CookieHelper
    {
        public static void setCookie(string cookieName, string cookieValue)
        {
            System.Web.HttpContext.Current.Response.Cookies[cookieName].Value = cookieValue;
        }
        public static string getCookie(string cookieName)
        {
            HttpCookie cookie = System.Web.HttpContext.Current.Request.Cookies[cookieName];
            if (cookie != null)
            {
                return cookie.Value;
            }
            return string.Empty;
        }
    }

    public class JsonToObjectCController : Controller
    {
        public static int treeNodeId = 0;
        private static Dictionary<string, StringBuilder> dictionary = new Dictionary<string, StringBuilder>();
        private static Dictionary<string, ConvertItem> convertList = new Dictionary<string, ConvertItem>();

        public static string Company
        {
            get
            {
                object author = CookieHelper.getCookie("JsonToObjectC_Company");
                if (author == null || string.IsNullOrEmpty(author.ToString()))
                {
                    return @"ToolWebCompany";
                }
                return author.ToString();
            }
        }
        public static string Project
        {
            get
            {
                object project = CookieHelper.getCookie("JsonToObjectC_Project");
                if (project == null || string.IsNullOrEmpty(project.ToString()))
                {
                    return @"ToolWebProject";
                }
                return project.ToString();
            }
        }
        public static string Author
        {
            get
            {
                object author = CookieHelper.getCookie("JsonToObjectC_Author");
                if (author == null || string.IsNullOrEmpty(author.ToString()))
                {
                    return @"ToolWebAuthor";
                }
                return author.ToString();
            }
        }
        private string GetNames(JToken token)
        {
            if (token == null) return string.Empty;

            string key;
            if (token.Parent == null)
            {
                dictionary.Clear();
                key = "DataModelInfo";
            }
            else if (token.Parent is JArray)
            {
                key = ((JProperty)token.Parent.Parent).Name + "ItemInfo";
            }
            else
            {
                key = ((JProperty)token.Parent).Name + "Info";
            }

            StringBuilder sb = new StringBuilder();
            dictionary[key] = sb;
            if (token != null)
            {
                string prefix = string.Empty;
                foreach (JProperty jp in token)
                {
                    if (jp.HasValues)
                    {
                        if (jp.Value.GetType() == typeof(JObject))
                        {
                            sb.AppendLine(string.Format("{0}@property (nonatomic, strong) {1} *{1};", prefix, jp.Name));
                            GetNames(jp.Value);
                        }
                        else if (jp.Value.GetType() == typeof(JArray))
                        {
                            sb.AppendLine(string.Format("{0}@property (nonatomic, strong) NSArray *{1};", prefix, jp.Name));
                            JToken itemValue = jp.Values().FirstOrDefault();
                            GetNames(itemValue);
                        }
                        else if (jp.Value.GetType() == typeof(JValue))
                        {
                            if (jp.Value.Type == JTokenType.Integer)
                            {
                                sb.AppendLine(string.Format("{0}@property (nonatomic, assign) NSInteger {1};", prefix, jp.Name));
                            }
                            else if (jp.Value.Type == JTokenType.Float)
                            {
                                sb.AppendLine(string.Format("{0}@property (nonatomic, strong) NSNumber *{1};", prefix, jp.Name));
                            }
                            else if (jp.Value.Type == JTokenType.Boolean)
                            {
                                sb.AppendLine(string.Format("{0}@property (nonatomic, assign) BOOL {1};", prefix, jp.Name));
                            }
                            else if (jp.Value.Type == JTokenType.String
                                || jp.Value.Type == JTokenType.Date
                                || jp.Value.Type == JTokenType.Guid
                                || jp.Value.Type == JTokenType.Uri
                                || jp.Value.Type == JTokenType.Null)
                            {
                                sb.AppendLine(string.Format("{0}@property (nonatomic, strong) NSString *{1};", prefix, jp.Name));
                            }
                            else
                            {
                                sb.AppendLine(prefix + "no handle name:" + jp.Name + "with value:" + ((JValue)jp.Value).Value);
                            }
                        }
                    }
                    else
                    {
                        sb.AppendLine(prefix + "no handle name:" + jp.Name + "because no values:");
                    }
                }
            }
            return sb.ToString();
        }
        private ZTreeNode GetZTreeNode(JToken token, string md5Key)
        {
            if (token == null) return null;

            string key;
            if (token.Parent == null)
            {
                dictionary.Clear();
                key = "DataModelInfo";
            }
            else if (token.Parent is JArray)
            {
                key = ((JProperty)token.Parent.Parent).Name + "ItemInfo";
            }
            else
            {
                key = ((JProperty)token.Parent).Name + "Info";
            }

            ZTreeNode treeNode = new ZTreeNode();
            treeNode.md5Key = md5Key;
            treeNode.originName =
                treeNode.name = key;
            treeNode.children = new List<ZTreeNode>();
            treeNode.id = treeNodeId;
            treeNode.isParent = true;
            treeNode.open = true;
            treeNode.CreateFileNode();
            if (token != null)
            {
                string prefix = string.Empty;
                foreach (JProperty jp in token)
                {
                    if (jp.HasValues)
                    {
                        if (jp.Value.GetType() == typeof(JObject))
                        {
                            ZTreeNode childrenTreeNode = this.GetZTreeNode(jp.Value, md5Key);
                            childrenTreeNode.id = ++treeNodeId;
                            childrenTreeNode.pId = treeNode.id;
                            childrenTreeNode.isParent = false;
                            childrenTreeNode.open = true;
                            if (childrenTreeNode != null)
                            {
                                treeNode.children.Add(childrenTreeNode);
                            }
                        }
                        else if (jp.Value.GetType() == typeof(JArray))
                        {
                            JToken itemValue = jp.Values().FirstOrDefault();
                            ZTreeNode childrenTreeNode = this.GetZTreeNode(itemValue, md5Key);
                            childrenTreeNode.id = ++treeNodeId;
                            childrenTreeNode.isParent = false;
                            childrenTreeNode.open = true;
                            if (childrenTreeNode != null)
                            {
                                treeNode.children.Add(childrenTreeNode);
                            }
                        }
                    }
                }
            }
            //if (treeNode.children.Count <= 0)
            //{
            //    treeNode.CreateFileNode();
            //}
            return treeNode;
        }
        //
        // GET: /JsonToObjectC/

        public ActionResult Index()
        {
            return View();
        }
        public ActionResult FileTree(RequestParam<List<JObject>> param)
        {
            try
            {
                List<object> treeNodes = new List<object>();
                foreach (JObject token in param.Data)
                {
                    this.GetConvertItem(token);
                    ZTreeNode treeNode = this.GetZTreeNode(token, GetMD5(token.ToString()));
                    treeNodes.Add(treeNode);
                }
                return Json(treeNodes);
            }
            catch (Exception ex)
            {
                return Json(new { Success = false, Message = ex.Message });
            }
        }
        public ActionResult Analyse()
        {
            return PartialView();
        }
        /// <summary>
        /// 转换代码
        /// </summary>
        /// <returns></returns>
        public ActionResult Translate()
        {
            try
            {
                JObject json = this.GetJsonObject().FirstOrDefault();
                ConvertItem convertItem = this.GetConvertItem(json);

                //var data = System.Web.HttpUtility.HtmlEncode(GenerateFileContent(convertItem.modelDic));
                //data = data.Replace("\r\n", "<br>");
                var data = RenderPartialViewToString(this, "TranslateTemplate", convertItem.modelDic);
                return Json(new { Success = true, Message = "操作成功", Data = data });
            }
            catch (Exception ex)
            {
                return Json(new { Success = false, Message = ex.Message });
            }
        }
        /// <summary>
        /// 下载代码
        /// </summary>
        /// <returns></returns>
        public ActionResult Download(RequestParam<DownloadParam> downloadParam)
        {
            try
            {
                JObject json = this.GetJsonObject(downloadParam.Data.json).FirstOrDefault();
                ConvertItem convertItem = this.GetConvertItem(json, true);
                if (string.IsNullOrWhiteSpace(convertItem.DownloadPath))
                {
                    string zipFilePath = GenerateFile(convertItem, downloadParam.Data.nodes, System.Web.HttpContext.Current);
                    string baseFilePath = Server.MapPath("/");
                    convertItem.DownloadPath = "/" + zipFilePath.Substring(baseFilePath.Length).Replace("\\", "/");
                }

                return Json(new { Success = true, Message = "操作成功", Data = convertItem.DownloadPath });
            }
            catch (Exception ex)
            {
                return Json(new { Success = false, Message = ex.Message });
            }
        }

        public ActionResult PreviewFile(ZTreeNode treeNode)
        {
            try
            {
                if (string.IsNullOrEmpty(treeNode.name))
                {
                    return Json(new { Success = false, Message = @"fileName is required!" });
                }
                if (string.IsNullOrEmpty(treeNode.md5Key))
                {
                    return Json(new { Success = false, Message = @"md5Key is required!" });
                }
                string name = Path.GetFileNameWithoutExtension(treeNode.name);
                string extension = Path.GetExtension(treeNode.name.ToLower());

                //if (extension != ".h" && extension != ".m")
                //{
                //    return Json(new { Success = false, Message = @"file extension is required (.m or .h)!" });
                //}

                string fileContent = null;
                ConvertItem convertItem = null;
                if (!string.IsNullOrEmpty(treeNode.dataJson))
                {
                    convertItem = this.GetConvertItem(this.GetJsonObject(treeNode.dataJson).FirstOrDefault(), true);
                }

                if (convertItem != null && convertItem.modelDic != null)
                {
                    var item = convertItem.modelDic.Where(t => t.Key.Equals(treeNode.originName)).FirstOrDefault();
                    if (item.Key == treeNode.originName)
                    {
                        if (extension == ".h")
                        {
                            fileContent = RenderPartialViewToString(this, "TranslateTemplate", new KeyValuePair<string, string>(treeNode.name, GenerateHFileContent(item)));
                        }
                        else if (extension == ".m")
                        {
                            fileContent = RenderPartialViewToString(this, "TranslateTemplate", new KeyValuePair<string, string>(treeNode.name, GenerateMFileContent(item)));
                        }
                        else
                        {
                            fileContent = RenderPartialViewToString(this, "TranslateTemplate", new KeyValuePair<string, StringBuilder>(treeNode.name, item.Value));
                        }
                    }
                }
                if (fileContent == null)
                {
                    return Json(new { Success = false, Message = @"没有找到对应的结点！" });
                }

                return Json(new { Success = true, Message = "操作成功", Data = fileContent });
            }
            catch (Exception ex)
            {
                return Json(new { Success = false, Message = ex.Message });
            }
        }
        private List<JObject> GetJsonObject()
        {
            return this.GetJsonObject(Request.Form["json"]);
        }
        private List<JObject> GetJsonObject(string jsonData)
        {
            if (string.IsNullOrEmpty(jsonData)) throw new Exception("请输入 JSON 字符串");
            if (jsonData.Trim().StartsWith("[") && jsonData.Trim().EndsWith("]"))
            {
                return JsonConvert.DeserializeObject<List<JObject>>(jsonData);
            }
            else
            {
                List<JObject> temp = new List<JObject>();
                temp.Add(JsonConvert.DeserializeObject<JObject>(jsonData));
                return temp;
            }
        }

        private ConvertItem GetConvertItem(JObject jsonObject)
        {
            return this.GetConvertItem(jsonObject, false);
        }
        private ConvertItem GetConvertItem(JObject jsonObject, bool update)
        {
            string md5 = GetMD5(jsonObject.ToString());
            if (convertList.ContainsKey(md5) && !update)
            {
                return convertList[md5];
            }
            convertList.Remove(md5);

            dictionary = new Dictionary<string, StringBuilder>();
            this.GetNames(jsonObject);

            if (dictionary.Count < 1)
            {
                return null;
            }

            ConvertItem convertItem = new ConvertItem() { MD5 = md5, JsonObject = jsonObject, modelDic = dictionary };
            convertList.Add(md5, convertItem);

            return convertItem;
        }

        #region file method
        public static string GenerateFileContent(Dictionary<string, StringBuilder> modelDic)
        {
            StringBuilder fileContent = new StringBuilder();
            foreach (var item in modelDic)
            {
                fileContent.AppendLine(GenerateHFileContent(item));
                fileContent.AppendLine(GenerateMFileContent(item));
            }
            return fileContent.ToString();
        }
        /// <summary>
        /// 生成.h文件内容
        /// </summary>
        /// <param name="keyValuePair"></param>
        /// <returns>.h文件内容</returns>
        public static string GenerateHFileContent(KeyValuePair<string, StringBuilder> keyValuePair)
        {
            StringBuilder fileContent = new StringBuilder();
            string dateString = DateTime.Now.ToString("yy/M/d");

            fileContent.AppendFormat(@"//
//  {0}.h
//  {4}
//
//  Created by {5} on {2}.
//  Copyright (c) {3}年 {6}. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface {0} : NSObject

{1}
#pragma mark - UI Property

@end
", keyValuePair.Key, keyValuePair.Value.ToString(), dateString, DateTime.Now.Year, Project, Author, Company);

            return fileContent.ToString();
        }
        /// <summary>
        /// 生成.m文件内容
        /// </summary>
        /// <param name="keyValuePair"></param>
        /// <returns>.m文件内容</returns>
        public static string GenerateMFileContent(KeyValuePair<string, StringBuilder> keyValuePair)
        {
            StringBuilder fileContent = new StringBuilder();
            string dateString = DateTime.Now.ToString("yy/M/d");

            fileContent.AppendFormat(@"//
//  {0}.m
//  {3}
//
//  Created by {4} on {1}.
//  Copyright (c) {2}年 {5}. All rights reserved.
//

#import ""{0}.h""

@implementation {0}

+ (NSArray *)ignoredPropertyNames
{{
    return @[];
}}

+ (NSArray *)ignoredCodingPropertyNames
{{
    return @[];
}}

@end
", keyValuePair.Key, dateString, DateTime.Now.Year, Project, Author, Company);

            return fileContent.ToString();
        }
        /// <summary>
        /// 生成下载的zip文件，并返回zip文件所在目录
        /// </summary>
        /// <param name="modelDic"></param>
        /// <returns>zip文件所在目录</returns>
        /*public static string GenerateFile(Dictionary<string, StringBuilder> modelDic, HttpContext context)
        {
            string dateName = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
            string tempDirectory = context.Server.MapPath("/Download/") + dateName;
            Directory.CreateDirectory(tempDirectory);

            foreach (var item in modelDic)
            {
                string hFileContent = GenerateHFileContent(item);
                using (FileStream fs = new FileStream(tempDirectory + "//" + item.Key + ".h", FileMode.Create))
                {
                    byte[] data = UTF8Encoding.UTF8.GetBytes(hFileContent); ;
                    fs.Write(data, 0, data.Length);
                }
                string mFileContent = GenerateMFileContent(item);
                using (FileStream fs = new FileStream(tempDirectory + "//" + item.Key + ".m", FileMode.Create))
                {
                    byte[] data = UTF8Encoding.UTF8.GetBytes(mFileContent); ;
                    fs.Write(data, 0, data.Length);
                }
            }
            try
            {
                string downloadDirectory = context.Server.MapPath("/Download/");
                if (!Directory.Exists(downloadDirectory))
                {
                    Directory.CreateDirectory(downloadDirectory);
                }

                string zipPath = downloadDirectory + "DataModel-" + dateName + ".zip";
                FastZip fz = new FastZip();
                fz.CreateEmptyDirectories = true;
                fz.CreateZip(zipPath, tempDirectory, true, "");
                fz = null;
                //delete temp directory
                Directory.Delete(tempDirectory, true);

                return zipPath;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }*/
        public static string GenerateFile(ConvertItem convertItem, List<ZTreeNode> treeNodes, HttpContext context)
        {
            return GenerateFile(convertItem, treeNodes, context, null);
        }
        public static string GenerateFile(ConvertItem convertItem, List<ZTreeNode> treeNodes, HttpContext context, string dateName)
        {
            bool isRoot = string.IsNullOrEmpty(dateName);
            if (isRoot)
            {
                dateName = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
            }
            string tempDirectory = context.Server.MapPath("/Download/") + dateName;
            Directory.CreateDirectory(tempDirectory);

            foreach (ZTreeNode treeNode in treeNodes)
            {
                var item = convertItem.modelDic.Where(t => t.Key.Equals(treeNode.originName)).FirstOrDefault();
                if (item.Key != treeNode.originName || !string.IsNullOrEmpty(treeNode.extention))
                {
                    continue;
                }

                var newItem = new KeyValuePair<string, StringBuilder>(treeNode.name, item.Value);
                string hFileContent = GenerateHFileContent(newItem);
                using (FileStream fs = new FileStream(tempDirectory + "//" + treeNode.name + ".h", FileMode.Create))
                {
                    byte[] data = UTF8Encoding.UTF8.GetBytes(hFileContent); ;
                    fs.Write(data, 0, data.Length);
                }
                string mFileContent = GenerateMFileContent(newItem);
                using (FileStream fs = new FileStream(tempDirectory + "//" + treeNode.name + ".m", FileMode.Create))
                {
                    byte[] data = UTF8Encoding.UTF8.GetBytes(mFileContent); ;
                    fs.Write(data, 0, data.Length);
                }

                if(treeNode.children!= null && treeNode.children.Count > 0)
                {
                    GenerateFile(convertItem, treeNode.children, context, dateName);
                }
            }
            if (isRoot)
            {
                try
                {
                    string downloadDirectory = context.Server.MapPath("/Download/");
                    if (!Directory.Exists(downloadDirectory))
                    {
                        Directory.CreateDirectory(downloadDirectory);
                    }

                    string zipPath = downloadDirectory + "DataModel-" + dateName + ".zip";
                    FastZip fz = new FastZip();
                    fz.CreateEmptyDirectories = true;
                    fz.CreateZip(zipPath, tempDirectory, true, "");
                    fz = null;
                    //delete temp directory
                    Directory.Delete(tempDirectory, true);

                    return zipPath;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
            {
                return null;
            }
        }

        #endregion

        private static string GetMD5(string str)
        {
            byte[] data = Encoding.Default.GetBytes(str);
            HashAlgorithm hashObj = new MD5CryptoServiceProvider();
            byte[] resultData = hashObj.ComputeHash(data);
            StringBuilder sBuild = new StringBuilder();
            for (int i = 0; i < resultData.Length; i++)
                sBuild.AppendFormat("{0:X2}", resultData[i]);
            return sBuild.ToString();
        }

        #region public static methods
        public static string GetString()
        {
            return string.Empty;
        }
        #endregion

        #region public helper methods
        /// <summary>
        /// 描述：输出View HTML 字符串
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="viewName">视图文件名</param>
        /// <param name="masterName">母板页文件名</param>
        /// <returns></returns>
        protected static string RenderViewToString(Controller controller, string viewName, string masterName)
        {
            IView view = ViewEngines.Engines.FindView(controller.ControllerContext, viewName, masterName).View;
            using (StringWriter writer = new StringWriter())
            {
                ViewContext viewContext = new ViewContext(controller.ControllerContext, view, controller.ViewData, controller.TempData, writer);
                viewContext.View.Render(viewContext, writer);
                return writer.ToString();
            }
        }

        /// <summary>
        /// 描述：输出PartialView HTML 字符串
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="partialViewName">部分视图文件名</param>
        /// <param name="model">数据</param>
        /// <returns></returns>
        protected static string RenderPartialViewToString(Controller controller, string partialViewName, object model)
        {
            IView view = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialViewName).View;
            controller.ViewData.Model = model;
            using (StringWriter writer = new StringWriter())
            {
                ViewContext viewContext = new ViewContext(controller.ControllerContext, view, controller.ViewData, controller.TempData, writer);
                viewContext.View.Render(viewContext, writer);
                return writer.ToString();
            }
        }

        #endregion
    }
}
